﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class GridMap : MonoBehaviourSingleton<GridMap>
{
    private GridNode[,] _grids;
    public Vector3 _gridSize;
    public float _nodeRadius;
    private float _nodeDiameter;
    public int _gridCntX;
    public int _gridCntZ;
    public bool _needShowGizmos = true;

    public GridNode[,] Grids
    {
        get
        {
            return _grids;
        }
    }

    #region 画辅助
    void OnDrawGizmos()
    {
        for (int j = 0; j < _gridCntZ; j++)
        {
            for (int i = 0; i < _gridCntX; i++)
            {
                if (_grids[i, j]._canWalk)
                    Gizmos.color = Color.white;
                else
                    Gizmos.color = Color.red;

                Gizmos.DrawCube(_grids[i, j]._worldPos, new Vector3(_nodeDiameter, 0, _nodeDiameter));

                Gizmos.color = Color.white;
                Gizmos.DrawWireCube(_grids[i, j]._worldPos, new Vector3(_nodeDiameter, 0, _nodeDiameter));
            }
        }
    }
    #endregion

    #region A * 相关
    private List<GridNode> m_lisOpen;
    private List<GridNode> m_lisClose;
    private GridNode _centerNode;

    public GridNode SearchPath(Vector3 from,Vector3 to, int confineNum = 100)
    {
        return SearchPath(GetNodeByPos(from), GetNodeByPos(to),confineNum);
    }
    public GridNode SearchPath(GridNode from,GridNode to,int confineNum=100)
    {
        int tempX = 0;
        int tempZ = 0;
        int searchNum = 0;
        m_lisOpen.Clear();
        m_lisClose.Clear();
        m_lisOpen.Add(from);

        while (m_lisOpen.Count != 0)
        {
            //获取F最小的那个
            _centerNode = GetMinF();
            m_lisClose.Add(_centerNode);
            m_lisOpen.Remove(_centerNode);
            searchNum++;
            if(searchNum >= confineNum)
            {
                return _centerNode;
            }

            for (int i = -1; i <= 1; i++)
            {
                for (int j = -1; j <= 1; j++)
                {
                    tempX = _centerNode._gridX + i;
                    tempZ = _centerNode._gridZ + j;
                    if(tempX >= 0 && tempX <_gridCntX &&
                       tempZ >= 0 && tempZ < _gridCntZ)
                    {
                        if (_grids[tempX, tempZ] == to)
                        {
                            Debug.Log("查找结点共花了: "+searchNum);
                            return _centerNode;
                        }
                        if (!m_lisClose.Contains(_grids[tempX, tempZ]) &&
                            _grids[tempX,tempZ]._canWalk)
                        {
                            if (m_lisOpen.Contains(_grids[tempX, tempZ]))
                            {
                                FoundPoint(_centerNode, _grids[tempX, tempZ]);
                            }
                            else
                            {
                                NotFoundPoint(_centerNode, _grids[tempX, tempZ], to);
                            }
                        }
                    }
                }
            }
            //if (m_lisOpen.Contains(to))
            //{
            //    return to;
            //}
        }
        return to;
    }
    //获取openList中F最小的那个
    private GridNode GetMinF()
    {
        GridNode min = null;
        for (int i = 0; i < m_lisOpen.Count; i++)
        {
            if (min == null)
                min = m_lisOpen[i];

            if (min.F > m_lisOpen[i].F)
                min = m_lisOpen[i];
        }
        return min;
    }
    //已经在Open中累积计算F值
    private void FoundPoint(GridNode cNode,GridNode rNode)
    {
        int G = CalG(cNode, rNode);
        if (G < rNode.G)
        {
            rNode._parent = cNode;
            rNode.G = G;
            rNode.CalF();
        }
    }

    //第一次检测到
    private void NotFoundPoint(GridNode cNode,GridNode rNode,GridNode eNode)
    {
        rNode._parent = cNode;
        rNode.G = CalG(cNode, rNode);
        rNode.H = CalH(rNode, eNode);
        rNode.CalF();
        m_lisOpen.Add(rNode);
    }
    //计算G
    private int CalG(GridNode from,GridNode to)
    {
        int result = 0;
        result = (int)(Vector3.Distance(from._worldPos, to._worldPos) * 100);
        result = from._parent != null ? (result + from._parent.G) : result;
        return result;
    }
    //计算H
    private int CalH(GridNode from, GridNode to)
    {
        int result = 0;
        result = (int)(Vector3.Distance(from._worldPos, to._worldPos) * 100);
        return result;
    }
    #endregion


    public void Init(Vector3 gridSize, float nodeRadius, bool needShowGizmos)
    {
        m_lisOpen = new List<GridNode>();
        m_lisClose = new List<GridNode>();
        _needShowGizmos = needShowGizmos;
        _gridSize = gridSize;
        _nodeRadius = nodeRadius;
        _nodeDiameter = _nodeRadius * 2;
        _gridCntX = Mathf.RoundToInt(_gridSize.x / _nodeDiameter);
        _gridCntZ = Mathf.RoundToInt(_gridSize.z / _nodeDiameter);
        _grids = new GridNode[_gridCntX, _gridCntZ];

        Vector3 startPoint = transform.position - _gridSize.x / 2 * Vector3.right - Vector3.forward * _gridSize.z / 2;
        for (int i = 0; i < _gridCntX; i++)
        {
            for (int j = 0; j < _gridCntZ; j++)
            {
                Vector3 worldPoint = startPoint + Vector3.right * (i * _nodeDiameter + _nodeRadius) + Vector3.forward * (j * _nodeDiameter + _nodeRadius);
                GridNode bn = new GridNode();
                bn.Init(true, worldPoint, i, j);
                _grids[i, j] = bn;
            }
        }
    }

    /// <summary>
    /// 获取 A 周边X单位的所有格子包括中心
    /// </summary>
    /// <param name="centerNode"></param>
    /// <param name="roundRange"></param>
    /// <param name="refLis"></param>
    public void GetRoundNode(GridNode centerNode,int roundRange,List<GridNode> refLis)
    {
        refLis.Clear();
        if(centerNode != null)
        {
            for (int i = -roundRange; i < roundRange; i++)
            {
                for (int j = -roundRange; j < roundRange; j++)
                {
                    int tempX = j + centerNode._gridX;
                    int tempZ = i + centerNode._gridZ;
                    if(tempX < _gridCntX && tempX >=0 &&
                       tempZ < _gridCntZ && tempZ >= 0)
                    {
                        refLis.Add(_grids[tempX, tempZ]);
                    }
                }
            }
        }
    }

    public GridNode GetNodeByPos(Vector3 pos)
    {
        float percentX = (pos.x + _gridSize.x / 2f) / _gridSize.x;
        float percentZ = (pos.z + _gridSize.z / 2f) / _gridSize.z;
        percentX = Mathf.Clamp01(percentX);
        percentZ = Mathf.Clamp01(percentZ);
        int x = Mathf.RoundToInt((_gridCntX - 1) * percentX);
        int z = Mathf.RoundToInt((_gridCntZ - 1) * percentZ);
        return _grids[x, z];
    }

    public List<GridNode> GetBattleNodeList(GridNode centerNode, int rowNum)
    {
        List<GridNode> battleNodeList = new List<GridNode>();
        if (centerNode == null)
        {
            return battleNodeList;
        }
        for (int i = -rowNum / 2; i <= rowNum / 2; i++)
        {
            for (int j = -rowNum / 2; j <= rowNum / 2; j++)
            {
                int tempX = centerNode._gridX + i;
                int tempZ = centerNode._gridZ + j;
                if (tempX < _gridCntX && tempX >= 0 && tempZ >= 0 && tempZ < _gridCntZ)
                {
                    if (!battleNodeList.Contains(_grids[tempX, tempZ]))
                    {
                        battleNodeList.Add(_grids[tempX, tempZ]);
                    }
                }
            }
        }
        return battleNodeList;
    }

    public GridNode[] GetNeibourhood(GridNode battleNode)
    {
        GridNode[] neibourhoods = new GridNode[8];
        int index = 0;
        for (int i = -1; i <= 1; i++)
        {
            for (int j = -1; j <= 1; j++)
            {
                index += 1;
                if (i == 0 && j == 0)
                {
                    continue;
                }
                int tempX = battleNode._gridX + i;
                int tempZ = battleNode._gridZ + j;
                if (tempX < _gridCntX && tempX >= 0 && tempZ >= 0 && tempZ < _gridCntZ)
                {
                    switch (index)
                    {
                        case 1:
                            neibourhoods[5] = _grids[tempX, tempZ];
                            break;
                        case 2:
                            neibourhoods[6] = _grids[tempX, tempZ];
                            break;
                        case 3:
                            neibourhoods[7] = _grids[tempX, tempZ];
                            break;
                        case 4:
                            neibourhoods[4] = _grids[tempX, tempZ];
                            break;
                        case 6:
                            neibourhoods[0] = _grids[tempX, tempZ];
                            break;
                        case 7:
                            neibourhoods[3] = _grids[tempX, tempZ];
                            break;
                        case 8:
                            neibourhoods[2] = _grids[tempX, tempZ];
                            break;
                        case 9:
                            neibourhoods[1] = _grids[tempX, tempZ];
                            break;
                    }
                }
            }
        }
        return neibourhoods;
    }



    /// <summary>
    /// 返回 以A-Node为中心的 周围 X单位格子的所有点,包含中心点
    /// </summary>
    /// <param name="centerNode"></param>
    /// <param name="rowNum"></param>
    /// <returns></returns>
    public List<GridNode> GetUnitNearNodeList(GridNode centerNode, int rowNum)
    {
        int tempZ;
        int tempX;
        List<GridNode> battleNodeList = new List<GridNode>();
        tempZ = centerNode._gridZ + rowNum / 2 + 1;
        for (int i = -rowNum / 2 - 1; i <= rowNum / 2 + 1; i++)
        {
            tempX = centerNode._gridX + i;
            if (tempX < _gridCntX && tempX >= 0 && tempZ >= 0 && tempZ < _gridCntZ)
            {
                if (!battleNodeList.Contains(_grids[tempX, tempZ]))
                {
                    battleNodeList.Add(_grids[tempX, tempZ]);
                }
            }
        }
        tempZ = centerNode._gridZ - rowNum / 2 - 1;
        for (int i = -rowNum / 2 - 1; i <= rowNum / 2 + 1; i++)
        {
            tempX = centerNode._gridX + i;
            if (tempX < _gridCntX && tempX >= 0 && tempZ >= 0 && tempZ < _gridCntZ)
            {
                if (!battleNodeList.Contains(_grids[tempX, tempZ]))
                {
                    battleNodeList.Add(_grids[tempX, tempZ]);
                }
            }
        }
        tempX = centerNode._gridX + rowNum / 2 + 1;
        for (int i = -rowNum / 2; i <= rowNum / 2; i++)
        {
            tempZ = centerNode._gridZ + i;
            if (tempX < _gridCntX && tempX >= 0 && tempZ >= 0 && tempZ < _gridCntZ)
            {
                if (!battleNodeList.Contains(_grids[tempX, tempZ]))
                {
                    battleNodeList.Add(_grids[tempX, tempZ]);
                }
            }
        }
        tempX = centerNode._gridX - rowNum / 2 - 1;
        for (int i = -rowNum / 2; i <= rowNum / 2; i++)
        {
            tempZ = centerNode._gridZ + i;
            if (tempX < _gridCntX && tempX >= 0 && tempZ >= 0 && tempZ < _gridCntZ)
            {
                if (!battleNodeList.Contains(_grids[tempX, tempZ]))
                {
                    battleNodeList.Add(_grids[tempX, tempZ]);
                }
            }
        }
        return battleNodeList;
    }

    public List<GridNode> GetRushFrontCheckNode(GridNode centerNode, int containNodeNum, int rushDirNum)
    {
        List<GridNode> returnBattleNodeList = new List<GridNode>();
        // 可以不写switch，todo
        switch (containNodeNum)
        {
            case 1:
                int targetZ = centerNode._gridZ + 1 * rushDirNum;
                if (targetZ >= 0 && targetZ < _gridCntZ)
                {
                    returnBattleNodeList.Add(Grids[centerNode._gridX, targetZ]);
                }
                break;
            case 3:
                targetZ = centerNode._gridZ + 2 * rushDirNum;
                if (targetZ >= 0 && targetZ < _gridCntZ)
                {
                    for (int i = centerNode._gridX - 1; i <= centerNode._gridX + 1; i++)
                    {
                        returnBattleNodeList.Add(Grids[i, targetZ]);
                    }
                }
                break;
            case 5:
                targetZ = centerNode._gridZ + 3 * rushDirNum;
                if (targetZ >= 0 && targetZ < _gridCntZ)
                {
                    for (int i = centerNode._gridX - 2; i <= centerNode._gridX + 2; i++)
                    {
                        returnBattleNodeList.Add(Grids[i, targetZ]);
                    }
                }
                break;
        }
        return returnBattleNodeList;
    }

    public List<GridObj> _needBeRushedUnitList;
    public int _nowNeedBeRushIndex;
    public List<GridObj> GetNeedBeRushedUnitList(int rushUnitIndex, GridObj beRushUnit, int rushDirNum)
    {
        _needBeRushedUnitList = new List<GridObj>();
        _needBeRushedUnitList.Add(beRushUnit);
        _nowNeedBeRushIndex = 0;
        GetAllBeRushUnitList(rushDirNum);
        return _needBeRushedUnitList;
    }

    private void GetAllBeRushUnitList(int rushDirNum)
    {
        //List<GridNode> beRushedNodeList = GetRushFrontCheckNode(_needBeRushedUnitList[_nowNeedBeRushIndex]._centerBattleNode, BattleSettings.Instance._battleUnitInfo[_needBeRushedUnitList[_nowNeedBeRushIndex]._troop._unitType]._useNodesNum, rushDirNum);
        List<GridNode> beRushedNodeList = new List<GridNode>();
        for (int i = 0; i < beRushedNodeList.Count; i++)
        {
            for (int j = 0; j < beRushedNodeList[i]._targetUnitList.Count; j++)
            {
                if (!_needBeRushedUnitList.Contains(beRushedNodeList[i]._targetUnitList[j]))
                {
                    _needBeRushedUnitList.Add(beRushedNodeList[i]._targetUnitList[j]);
                }
            }
        }
        _nowNeedBeRushIndex++;
        if (_nowNeedBeRushIndex < _needBeRushedUnitList.Count)
        {
            GetAllBeRushUnitList(rushDirNum);
        }
    }
}
